package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"

	"104get/iec104"
	"104get/until"

	"github.com/bitly/go-simplejson"
	"github.com/iancoleman/orderedmap"
	"github.com/nsf/termbox-go"
	"golang.org/x/crypto/ssh"
)

var (
	// 存储遥测数据的map key为地址，value为值
	YCtelemetryMap map[string]string
	// 存储地址与字段名的映射 key为地址，value为字段名
	YCaddressMap = orderedmap.New()

	// 存储遥信数据的map key为地址，value为值
	YXtelemetryMap map[string]string
	// 存储地址与字段名的映射 key为地址，value为字段名
	YXaddressMap = orderedmap.New()

	// 存储当前读取数据的日志时间
	lineTime string
	// 存储当前读取数据
	lineText string
	// 程序的启动时间，2021-01-01~00:00:00,string格式
	startTime = time.Now().Format("01-02-15-04") + ".log"

	confJson, _ = until.ReadFileAndReturnJSON("conf.json")
)

// 打印telemetryMap,传入参为 两个地址,是打印的起始地址和结束地址
func PrintTelemetryMap() {
	// 打印telemetryMap,而且不要打印完一组数据就换行,多打印几个再换行,多个数据之间用制表符隔开
	// 遍历telemetryMap
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	// 从addressMap中截取出要打印的数据
	var YCaddressMap2 = orderedmap.New()
	var YXaddressMap2 = orderedmap.New()
	// 遍历addressMap
	for _, k := range YCaddressMap.Keys() {
		// 判断是否在范围内
		if until.CompareHexStr(k,
			confJson.Get("ycpointFirst").MustString(),
			confJson.Get("ycpointEnd").MustString()) {
			name, _ := YCaddressMap.Get(k)
			YCaddressMap2.Set(k, name)
		}
	}
	for _, k := range YXaddressMap.Keys() {
		// 判断是否在范围内
		if until.CompareHexStr(k,
			confJson.Get("yxpointFirst").MustString(),
			confJson.Get("yxpointEnd").MustString()) {
			name, _ := YXaddressMap.Get(k)
			YXaddressMap2.Set(k, name)
		}
	}

	for i, k := range YCaddressMap2.Keys() {
		name, _ := YCaddressMap.Get(k)
		value := YCtelemetryMap[k]
		printdata := fmt.Sprintf("%s: %-18s: %s", k, name, value)
		// 根据终端大小自适应打印,竖着往下打印,每行打印一个数据,当终端高度不够时,换另一列打印
		// 获取终端大小
		_, height := termbox.Size()
		// 计算打印的位置
		x := i / height
		y := i % height
		print(x*40, y, termbox.ColorDefault, termbox.ColorDefault, printdata)
	}
	for i, k := range YXaddressMap2.Keys() {
		name, _ := YXaddressMap.Get(k)
		value := YXtelemetryMap[k]
		printdata := fmt.Sprintf("%-2s: %-18s: %s", k, name, value)
		// 与YC数据分开，另起一列打印
		// 获取终端大小
		_, height := termbox.Size()
		// 计算打印的位置,先计算YC数据的列数
		x := len(YCaddressMap2.Keys())/height + 1 + i/height
		// 计算打印的位置
		y := i % height
		print(x*40, y, termbox.ColorDefault, termbox.ColorDefault, printdata)
	}
	// 紧接着在遥信最后一行打印时间
	len1 := len(YCaddressMap2.Keys())
	len2 := len(YXaddressMap2.Keys())
	_, height := termbox.Size()
	// 计算出x和y
	x := (len1+1)/height + 1 + len2/height
	// 算出遥信的y
	y := len2%height + 1
	print(x*40, y, termbox.ColorDefault, termbox.ColorDefault, lineTime)

	// 在刷新终端前，将即将显示到终端中的画面从缓冲区存储到json中，然后写入到文件中
	/*
		jsonData := simplejson.New()
		jsonData.Set("lineTime", lineTime)
		jsonData.Set("lineText", lineText)
		jsonData.Set("time", time.Now().Format("2006-01-02 15:04:05"))
		// 在jsonData中创两个数组对象，一个存储YC数据，一个存储YX数据
		YCdata := simplejson.New()
		YXdata := simplejson.New()

		for _, k := range YCaddressMap2.Keys() {
			name, _ := YCaddressMap.Get(k)
			value := YCtelemetryMap[k]
			data := simplejson.New()
			data.Set("name", name)
			data.Set("value", value)
			YCdata.Set(k, data)
		}
		jsonData.Set("YCdata", YCdata)
		for _, k := range YXaddressMap2.Keys() {
			name, _ := YXaddressMap.Get(k)
			value := YXtelemetryMap[k]
			data := simplejson.New()
			data.Set("name", name)
			data.Set("value", value)
			YXdata.Set(k, data)
		}
		jsonData.Set("YXdata", YXdata)
		// 写入到文件中
		data, _ := json.MarshalIndent(jsonData, "", "    ")
		until.AppendToFile(startTime, string(data)+"\n")
	*/
	termbox.Flush()
}

// termbox打印的封装
func print(x, y int, fg, bg termbox.Attribute, msg string) {
	for i, c := range msg {
		termbox.SetCell(x+i, y, c, fg, bg)
	}
}

func main() {
	// 初始化
	YCtelemetryMap = make(map[string]string)
	YXtelemetryMap = make(map[string]string)
	ParseAddressMap()   //解析出地址与字段名的映射
	go listenKeyboard() //监听键盘输入
	if err := termbox.Init(); err != nil {
		log.Fatalf("初始化termbox失败: %v", err)
	}

	ip := confJson.Get("ip").MustString()
	port := confJson.Get("port").MustString()
	user := confJson.Get("user").MustString()
	pass := confJson.Get("pass").MustString()
	cmd := confJson.Get("cmd").MustString()

	// 建立ssh连接
	sshClient, err := ssh.Dial("tcp", ip+":"+port, &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{ssh.Password(pass)},
		HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
			return nil
		},
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	defer sshClient.Close()

	// 建立ssh会话
	session, err := sshClient.NewSession()
	if err != nil {
		fmt.Println(err)
		return
	}
	defer session.Close()

	// 执行命令
	stdout, err := session.StdoutPipe()
	if err != nil {
		fmt.Println(err)
		return
	}

	// 执行一个前台命令，并实时读取输出
	if err := session.Start(cmd); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("开始执行命令：", cmd)
	// 实时逐行读取输出
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		handleLine(scanner.Text())
		// 打印遥测数据
		PrintTelemetryMap()
	}

	// 等待命令执行完毕
	if err := session.Wait(); err != nil {
		// 打印报错
		until.AppendToFile(startTime, "执行命令："+cmd+" 报错："+err.Error()+"\n")
		return
	}
	termbox.Close()
	fmt.Println("命令执行完毕")
}

// 处理每行的输出
func handleLine(line string) {
	// 从每行中提取出[Tx]: 后面的内容
	index := until.FindTagIndex(line, "[Tx]: ")
	if index == -1 {
		return
	}
	// 提取出开头的时间
	timeIndex := until.FindTagIndex(line, "[")
	if timeIndex == -1 {
		return
	}
	// 提取出时间
	timeIndexEnd := strings.Index(line[timeIndex:], "]")
	if timeIndexEnd == -1 {
		return
	}
	// 提取出时间
	timeStr := line[timeIndex : timeIndex+timeIndexEnd-1] //读取行最左边的时间部分
	lineTime = timeStr
	lineText = line[index:] // 读取行最右边的数据部分
	// 解析apdu
	apdu := new(iec104.APDU)
	if apdu.ParseAPDU(until.StringToHex(line[index:])) != nil {
		return
	}

	// 将apdu对象转为json格式
	jsonSignals, err := json.MarshalIndent(apdu.Signals, "", "    ")
	if err != nil {
		fmt.Println(err)
		return
	}
	// 再通过simplejson库解析json数据
	j, err := simplejson.NewJson(jsonSignals)
	if err != nil {
		fmt.Println(err)
		return
	}
	YCdatatmp := orderedmap.New()
	YXdatatmp := orderedmap.New()
	for i := 0; i < len(j.MustArray()); i++ {
		if j.GetIndex(i).Get("type_id").MustInt() == 13 {
			// 取出Address
			address := j.GetIndex(i).Get("address").MustInt()
			// 取出Value
			value := j.GetIndex(i).Get("value").MustFloat64()
			YCdatatmp.Set(until.IntToHex(address), strconv.FormatFloat(value, 'f', -1, 32))
			// 存储遥测数据，value按照float32存储
			YCtelemetryMap[until.IntToHex(address)] = strconv.FormatFloat(value, 'f', -1, 32)
		} else if j.GetIndex(i).Get("type_id").MustInt() == 1 {
			// 取出Address
			address := j.GetIndex(i).Get("address").MustInt()
			// 取出Value
			value := j.GetIndex(i).Get("value").MustInt()
			YXdatatmp.Set(until.IntToHex(address), strconv.Itoa(value))
			// 存储遥信数据
			YXtelemetryMap[until.IntToHex(address)] = strconv.Itoa(value)
		}
	}

	// 将本条数据解析情况存放到本地文件中
	jsonData := simplejson.New()
	jsonData.Set("lineTime", lineTime)
	jsonData.Set("lineText", lineText)
	jsonData.Set("time", time.Now().Format("2006-01-02 15:04:05"))
	// 在jsonData中创两个数组对象，一个存储YC数据，一个存储YX数据
	YCdata := simplejson.New()
	YXdata := simplejson.New()

	for _, k := range YCdatatmp.Keys() {
		name, _ := YCaddressMap.Get(k)
		value := YCtelemetryMap[k]
		data := simplejson.New()
		data.Set("name", name)
		data.Set("value", value)
		YCdata.Set(k, data)
	}
	jsonData.Set("YCdata", YCdata)
	for _, k := range YXdatatmp.Keys() {
		name, _ := YXaddressMap.Get(k)
		value := YXtelemetryMap[k]
		data := simplejson.New()
		data.Set("name", name)
		data.Set("value", value)
		YXdata.Set(k, data)
	}
	jsonData.Set("YXdata", YXdata)
	// 写入到文件中
	data, _ := json.MarshalIndent(jsonData, "", "    ")
	until.AppendToFile(startTime, string(data)+"\n")
}

// 从json中解析出地址与字段名的映射,并存储到addressMap中
func ParseAddressMap() {
	js, err := until.ReadFileAndReturnJSON("104param.json")
	if err != nil {
		fmt.Println(err)
		return
	}
	dataset := js.Get("104cfg").Get("dataset")
	for i := 0; i < len(dataset.MustArray()); i++ {
		// 取出datatype为YC的data
		if dataset.GetIndex(i).Get("datatype").MustString() == "YC" {
			data := dataset.GetIndex(i).Get("data")
			for j := 0; j < len(data.MustArray()); j++ {
				// 取出addr和name
				addr := data.GetIndex(j).Get("addr").MustString()
				name := data.GetIndex(j).Get("name").MustString()
				// 存储到addressMap中
				YCaddressMap.Set(addr, name)
			}
		} else if dataset.GetIndex(i).Get("datatype").MustString() == "YX" {
			data := dataset.GetIndex(i).Get("data")
			for j := 0; j < len(data.MustArray()); j++ {
				// 取出addr和name
				addr := data.GetIndex(j).Get("addr").MustString()
				name := data.GetIndex(j).Get("name").MustString()
				// 存储到addressMap中
				YXaddressMap.Set(addr, name)
			}
		}
	}
}

// 持续监听键盘输入
func listenKeyboard() {
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		// 监听到ctrl+c则退出程序
		case termbox.EventKey:
			if ev.Key == termbox.KeyCtrlC {
				//清屏用termbox
				// termbox.Close()
				os.Exit(0)
			}
		}
	}
}
